import { Dialog } from 'quasar'
import { defineAsyncComponent } from 'vue'
import { arrayToMap } from '@/common/utils'

const numberExpressList = [
  '@natural(60, 100)', '@integer', '@float(60, 100, 3, 5)',
]

const booleanExpressList = [ '@boolean' ]

const rootTypes = [
  {
    label: 'Object',
    value: 'object',
    mockTip: [
      undefined, '1', '1-3',
    ],
  },
  {
    label: 'Array',
    value: 'array',
    mockTip: [
      '1-5', '5-25', 5, 15, '+1',
    ],
  },
]

const objectTypes = Object.freeze(rootTypes.map(d => d.value))

const dataTypes = [
  {
    label: 'String',
    value: 'string',
    mockTip: getMockExpressListByAttr,
  },
  {
    label: 'Number',
    value: 'number',
    mockTip: numberExpressList,
  },
  {
    label: 'Boolean',
    value: 'boolean',
    mockTip: booleanExpressList,
  },
  ...rootTypes,
]

const typeMap = arrayToMap(dataTypes)

const mockExpressInfo = {
  cname: '中文名',
  name: '英文名',
  ctitle: '中文标题',
  csentence: '中文句子',
  cparagraph: '中文段落',
  title: '英文标题',
  paragraph: '英文段落',
  sentence: '英文句子',
  id: '18位随机数',
  guid: '全局唯一标识符',
  date: '日期',
  datetime: '日期时间',
  time: '时间',
  now: '当前时间（精确到秒 ）',
  pick: '随机选择数组中的一个',
  email: '邮箱',
  dataImage: 'base64图片',
  url: '链接',
  zip: '邮政编码',
  ip: 'ip地址',
  color: '十六进制颜色',
  hex: '十六进制颜色',
  rgb: 'RGB三原色',
  rgba: '含透明度的RGB三原色',
  hsl: '色相、饱和度、亮度',
}

const keyExpressMap = {
  'user name': [ '@cname', '@name' ],
  'title desc introduction remark': [
    '@ctitle(3, 15)', '@csentence(3, 5)', '@cparagraph(1, 3)', '@title(3, 15)', '@paragraph(1, 3)', '@sentence(3, 5)',
  ],
  'key id': [ '@id', '@guid' ],
  'time date day': [
    '@timestamp', '@date("yyyy-MM-dd")', '@datetime("yyyy-MM-dd HH:mm:ss")', '@now', '@time("HH:mm:ss")',
  ],
  'type level': [ '@pick(["1", 2, {type: 3}])' ],
  'count sum num number total age': numberExpressList,
  'email mail': [ '@email' ],
  'image img picture photo graph': [ '@dataImage(200x200, \'测试图片\')', 'https://picsum.photos/900/1200' ],
  'url link': [ '@url' ],
  'zip postal': [ '@zip' ],
  'address': [
    '@province', '@city', '@county',
  ],
  'ip': [ '@ip' ],
  'color': [
    '@color', '@hex', '@rgb', '@rgba', '@hsl',
  ],
  'if is': booleanExpressList,
}

const allExpressList = Object.values(keyExpressMap).reduce((arr, v) => {
  arr.push(...v)
  return arr
}, [])

function getMockExpressListByAttr(attr) {
  let r = []
  if (attr) {
    const attrLower = attr.toLowerCase()
    for (const keys in keyExpressMap) {
      if (keys.split(/\s/).some(k => attrLower.includes(k))) {
        r = keyExpressMap[keys]
        break
      }
    }
  }
  return [ ...new Set([ ...r, ...allExpressList ]) ]
}

function getMockExpressListByAttrWithInfo(attr) {
  const list = getMockExpressListByAttr(attr)
  return list?.map(mock => {
    return {
      mock,
      info: mockExpressInfo[String(mock).match(/^@(\w+)/)?.[1]],
    }
  })
}

function showMockTip() {
  Dialog.create({
    component: defineAsyncComponent(() => import('@@/MockTip.vue')),
  })
}

function getSuggestList(row, rootType) {
  const type = row?.type?.value || row?.type || rootType
  let suggestion = typeMap.get(type)?.mockTip
  if (suggestion instanceof Function) {
    suggestion = suggestion(row?.attr)
  }
  if (type === 'object' || type === 'array') {
    return suggestion
  }
  return [ ...new Set([ ...(suggestion || []), ...allExpressList ]) ]
}

function inferTypeByMock(mock) {
  if ([
    'string', 'number', 'boolean',
  ].includes(typeof mock)) {
    const mockStr = String(mock)
    if (booleanExpressList.map(e => String(e)).includes(mockStr)) {
      return typeMap.get('boolean')
    }
    const fnReg = /\(.*$/
    if (!isNaN(Number(mock)) || numberExpressList.map(e => e.replace(fnReg, '')).includes(mockStr.replace(fnReg, ''))) {
      return typeMap.get('number')
    }
  }

  return typeMap.get('string')
}

export {
  rootTypes,
  objectTypes,
  dataTypes,
  typeMap,
  showMockTip,
  getMockExpressListByAttr,
  getMockExpressListByAttrWithInfo,
  numberExpressList,
  booleanExpressList,
  allExpressList,
  keyExpressMap,
  getSuggestList,
  inferTypeByMock,
}
